/*****************************************************************************
 * plum_mbedtls_base64.c
 *
 * Copyright (C) 2020 Jeasonvor 1101627719@qq.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ****************************************************************************/

/*****************************************************************************
 * Included Files
 ****************************************************************************/
#include "plum_api.h"
/*****************************************************************************
 * Trace Definitions
 ****************************************************************************/
#define LOG_RUN_LEVEL LOG_LEVEL_DEBUG
#define LOG_MODULE    "mbedtls.base64"
#include "thirdparty/log/log.h"
/*****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/
#define MBEDTLS_BASE64_SPACE '='
/*****************************************************************************
 * Private Types
 ****************************************************************************/

/*****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/*****************************************************************************
 * Private Data
 ****************************************************************************/
PLUM_PRIVATE
PLUM_CONST plum_u8 base64_enc_map[64] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};

PLUM_PRIVATE
PLUM_CONST plum_u8 base64_dec_map[128] = {
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 62,  127,
    127, 127, 63,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  127, 127,
    127, 64,  127, 127, 127, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
    10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
    25,  127, 127, 127, 127, 127, 127, 26,  27,  28,  29,  30,  31,  32,  33,
    34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
    49,  50,  51,  127, 127, 127, 127, 127};

/*****************************************************************************
 * Public Data
 ****************************************************************************/

/*****************************************************************************
 * Private Functions
 ****************************************************************************/

PLUM_PRIVATE
plum_s32 cal_mbedtls_base64_decode_chr(PLUM_CONST plum_s8 *str, plum_u8 c)
{
    plum_s8 *ptr = strchr(str, c);
    if (!ptr) {
        return (0);
    }

    return (ptr - str);
}

/*****************************************************************************
 * Public Functions
 ****************************************************************************/

PLUM_PUBLIC
plum_s32 plum_cal_mbedtls_base64_encode(plum_u8 *src, plum_u32 inlen,
                                        plum_u8 *dst, plum_u32 *outlen)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if ((inlen == 0) || (*outlen == 0)) {
            rc = PLUM_ECODE_EPARA;
            LOG_E("input/output lenth is zero");
            break;
        }

        if (!src || !dst) {
            rc = PLUM_ECODE_INVALID;
            LOG_E("input/output pointer is null");
            break;
        }

        plum_u32 cnt = (inlen / 3) + ((inlen % 3) != 0);
        if (*outlen < ((cnt * 4) + 1)) {
            rc = PLUM_ECODE_EMEM;
            LOG_E("destination buffer is too small");
            break;
        }

        plum_u32 loop = 0;
        cnt           = inlen / 3;
        plum_u8  c1, c2, c3;
        plum_u8 *d = dst, *s = src;
        for (loop = 0; loop < cnt; loop++) {
            c1 = *s++;
            c2 = *s++;
            c3 = *s++;

            *d++ = base64_enc_map[c1 >> 2];
            *d++ = base64_enc_map[((c1 & 0x03) << 4) | (c2 >> 4)];
            *d++ = base64_enc_map[((c2 & 0x0F) << 2) | (c3 >> 6)];
            *d++ = base64_enc_map[c3 & 0x3F];
        }
        *outlen = cnt << 2;

        cnt = inlen % 3;
        switch (cnt) {
            case 0:
                break;

            case 1:
                c1 = *src++;

                // *d++ = base64_enc_map[c1 >> 2];
                *d++ = base64_enc_map[(c1 & 0x03) << 4];
                *d++ = MBEDTLS_BASE64_SPACE;
                *d++ = MBEDTLS_BASE64_SPACE;

                *outlen += 4;
                break;

            case 2:
                //  c1 = *src++;
                c2 = *src++;

                // *d++ = base64_enc_map[c1 >> 2];
                //*d++ = base64_enc_map[((c1 & 0x03) << 4) | c2 >> 4];
                *d++ = base64_enc_map[(c2 & 0x0F) << 2];
                *d++ = MBEDTLS_BASE64_SPACE;

                *outlen += 4;
                break;
        }

        *d = 0;
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_cal_mbedtls_base64_decode(plum_u8 *src, plum_u32 inlen,
                                        plum_u8 *dst, plum_u32 *outlen)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if ((inlen == 0) || (*outlen == 0)) {
            rc = PLUM_ECODE_EPARA;
            LOG_E("input lenth is zero");
            break;
        }

        if (!src || !dst) {
            rc = PLUM_ECODE_INVALID;
            LOG_E("input/output pointer is null");
            break;
        }

        plum_u32 cnt = (inlen >> 2) + ((inlen % 4) != 0);
        if (*outlen < ((cnt * 3) + 1)) {
            rc = PLUM_ECODE_EMEM;
            LOG_E("destination buffer is too small");
            break;
        }

        plum_s32 c1, c2, c3, c4;
        plum_u32 loop;
        for (loop = 0; loop < inlen;) {
            LOG_D("loop : %d", loop);
            c1 = cal_mbedtls_base64_decode_chr(base64_enc_map, src[loop++]);
            LOG_D("c1 :%c -> %d", src[loop - 1], c1);
            c2 = cal_mbedtls_base64_decode_chr(base64_enc_map, src[loop++]);
            LOG_D("c2 :%c -> %d", src[loop - 1], c2);
            *dst++ = (c1 << 2) | ((c2 & 0x3F) >> 4);
            *outlen++;
            if (src[loop - 1] == MBEDTLS_BASE64_SPACE) {
                break;
            }

            c3 = cal_mbedtls_base64_decode_chr(base64_enc_map, src[loop++]);
            LOG_D("c3 :%c -> %d", src[loop - 1], c3);
            *dst++ = (c2 << 4) | ((c3 & 0x3F) >> 2);
            *outlen++;

            c4 = cal_mbedtls_base64_decode_chr(base64_enc_map, src[loop++]);
            LOG_D("c4 :%c -> %d", src[loop - 1], c4);
            *dst++ = ((c3 & 0x03) << 6) | (c4 & 0x3F);
            *outlen++;
        }

    } while (0);

    return (rc);
}

/****************************************************************************/
/*                                                                          */
/*  End of file.                                                            */
/*                                                                          */
/****************************************************************************/
